diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-12-01 13:26:44 +0100 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-12-01 17:23:19 +0100 |
| commit | dfdbf6cac1fe3719dc71e130129d28e04ba4e225 (patch) | |
| tree | f865bdad53cef95bdfb10fc04174a0173ab36f15 /src/pages/thematique/[slug].tsx | |
| parent | 5b762b1b669454a89899c4bdf6008027d9615acf (diff) | |
refactor(pages): refine Thematic pages
* add a table of contents (however posts heading are
not included)
* rename posts list section title
* add a useThematic hook to refresh thematic contents
* add a useThematicLists hook to refresh thematics list
* add a `notIn` filter in thematics list fetcher to directly
remove unwanted thematics
* add Cypress tests
Diffstat (limited to 'src/pages/thematique/[slug].tsx')
| -rw-r--r-- | src/pages/thematique/[slug].tsx | 181 |
1 files changed, 119 insertions, 62 deletions
diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx index 487b18b..e290782 100644 --- a/src/pages/thematique/[slug].tsx +++ b/src/pages/thematique/[slug].tsx @@ -14,17 +14,24 @@ import { PageHeader, PageSidebar, PageBody, + LoadingPage, + TocWidget, + Spinner, } from '../../components'; import { convertWPThematicPreviewToPageLink, - convertWPThematicToThematic, fetchAllThematicsSlugs, fetchThematic, fetchThematicsCount, fetchThematicsList, } from '../../services/graphql'; import styles from '../../styles/pages/blog.module.scss'; -import type { NextPageWithLayout, PageLink, Thematic } from '../../types'; +import type { + GraphQLConnection, + NextPageWithLayout, + WPThematic, + WPThematicPreview, +} from '../../types'; import { CONFIG } from '../../utils/config'; import { ROUTES } from '../../utils/constants'; import { @@ -33,29 +40,47 @@ import { getSchemaJson, getSinglePageSchema, getWebPageSchema, + slugify, } from '../../utils/helpers'; import { loadTranslation, type Messages } from '../../utils/helpers/server'; -import { useBreadcrumb } from '../../utils/hooks'; +import { + useBreadcrumb, + useHeadingsTree, + useThematic, + useThematicsList, +} from '../../utils/hooks'; export type ThematicPageProps = { - currentThematic: Thematic; - thematics: PageLink[]; + data: { + currentThematic: WPThematic; + otherThematics: GraphQLConnection<WPThematicPreview>; + totalThematics: number; + }; translation: Messages; }; -const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ - currentThematic, - thematics, -}) => { - const { content, intro, meta, slug, title } = currentThematic; - const { articles, dates, seo, relatedTopics } = meta; +const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ data }) => { const intl = useIntl(); + const { isFallback } = useRouter(); + const { isLoading, thematic } = useThematic( + data.currentThematic.slug, + data.currentThematic + ); + const { isLoading: areThematicsLoading, thematics } = useThematicsList({ + fallback: data.otherThematics, + input: { first: data.totalThematics, where: { notIn: [thematic.id] } }, + }); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ - title, - url: `${ROUTES.THEMATICS}/${slug}`, + title: data.currentThematic.title, + url: `${ROUTES.THEMATICS}/${data.currentThematic.slug}`, }); + const { ref, tree } = useHeadingsTree({ fromLevel: 2 }); + + if (isFallback || isLoading) return <LoadingPage />; + + const { content, intro, meta, slug, title } = thematic; + const { articles, dates, seo, relatedTopics } = meta; - const { asPath } = useRouter(); const webpageSchema = getWebPageSchema({ description: seo.description, locale: CONFIG.locales.defaultLocale, @@ -74,18 +99,41 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ }); const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]); - const thematicsListTitle = intl.formatMessage({ - defaultMessage: 'Other thematics', - description: 'ThematicPage: other thematics list widget title', - id: 'KVSWGP', - }); + const messages = { + widgets: { + loadingThematicsList: intl.formatMessage({ + defaultMessage: 'Thematics are loading...', + description: 'ThematicPage: loading thematics message', + id: 'rVoW4G', + }), + thematicsListTitle: intl.formatMessage({ + defaultMessage: 'Other thematics', + description: 'ThematicPage: other thematics list widget title', + id: 'KVSWGP', + }), + tocTitle: intl.formatMessage({ + defaultMessage: 'Table of Contents', + description: 'PageLayout: table of contents title', + id: 'eys2uX', + }), + topicsListTitle: intl.formatMessage({ + defaultMessage: 'Related topics', + description: 'ThematicPage: related topics list widget title', + id: '/42Z0z', + }), + }, + browsePostsTitle: intl.formatMessage( + { + defaultMessage: 'Browse posts in {thematicName} thematic', + description: 'ThematicPage: posts list heading', + id: 'jrRBeb', + }, + { thematicName: title } + ), + }; - const topicsListTitle = intl.formatMessage({ - defaultMessage: 'Related topics', - description: 'ThematicPage: related topics list widget title', - id: '/42Z0z', - }); - const pageUrl = `${CONFIG.url}${asPath}`; + const pageUrl = `${CONFIG.url}${slug}`; + const browsePostHeadingId = slugify(messages.browsePostsTitle); return ( <Page breadcrumbs={breadcrumbItems}> @@ -121,23 +169,33 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ updateDate: dates.update, }} /> - <PageBody className={styles.body}> - {/*eslint-disable-next-line react/no-danger -- Necessary for content*/} - <div dangerouslySetInnerHTML={{ __html: content }} /> + <PageSidebar> + <TocWidget + heading={<Heading level={2}>{messages.widgets.tocTitle}</Heading>} + tree={[ + ...tree, + { + children: [], + depth: 2, + id: browsePostHeadingId, + label: messages.browsePostsTitle, + }, + ]} + /> + </PageSidebar> + <PageBody> + <div + className={styles.body} + // eslint-disable-next-line react/no-danger -- Necessary for content + dangerouslySetInnerHTML={{ __html: content }} + ref={ref} + /> {articles ? ( <> - <Heading level={2}> - {intl.formatMessage( - { - defaultMessage: 'All posts in {thematicName}', - description: 'ThematicPage: posts list heading', - id: 'LszkU6', - }, - { thematicName: title } - )} + <Heading id={browsePostHeadingId} level={2}> + {messages.browsePostsTitle} </Heading> <PostsList - className={styles.list} posts={getPostsWithUrl(articles)} headingLvl={3} sortByYear @@ -146,20 +204,24 @@ const ThematicPage: NextPageWithLayout<ThematicPageProps> = ({ ) : null} </PageBody> <PageSidebar> - <LinksWidget - heading={ - <Heading isFake level={3}> - {thematicsListTitle} - </Heading> - } - items={getLinksItemData(thematics)} - /> + {areThematicsLoading ? ( + <Spinner>{messages.widgets.loadingThematicsList}</Spinner> + ) : ( + <LinksWidget + heading={ + <Heading level={2}>{messages.widgets.thematicsListTitle}</Heading> + } + items={getLinksItemData( + thematics.edges.map((edge) => + convertWPThematicPreviewToPageLink(edge.node) + ) + )} + /> + )} {relatedTopics ? ( <LinksWidget heading={ - <Heading isFake level={3}> - {topicsListTitle} - </Heading> + <Heading level={2}>{messages.widgets.topicsListTitle}</Heading> } items={getLinksItemData(relatedTopics)} /> @@ -179,26 +241,21 @@ export const getStaticProps: GetStaticProps<ThematicPageProps> = async ({ locale, params, }) => { - const currentThematic = await fetchThematic((params as ThematicParams).slug); + const thematic = await fetchThematic((params as ThematicParams).slug); const totalThematics = await fetchThematicsCount(); - const allThematicsEdges = await fetchThematicsList({ + const otherThematics = await fetchThematicsList({ first: totalThematics, + where: { notIn: [thematic.databaseId] }, }); - const allThematics = allThematicsEdges.edges.map((edge) => - convertWPThematicPreviewToPageLink(edge.node) - ); - const allThematicsLinks = allThematics.filter( - (thematic) => - thematic.url !== `${ROUTES.THEMATICS}/${(params as ThematicParams).slug}` - ); const translation = await loadTranslation(locale); return { props: { - currentThematic: JSON.parse( - JSON.stringify(convertWPThematicToThematic(currentThematic)) - ), - thematics: JSON.parse(JSON.stringify(allThematicsLinks)), + data: { + currentThematic: JSON.parse(JSON.stringify(thematic)), + otherThematics: JSON.parse(JSON.stringify(otherThematics)), + totalThematics, + }, translation, }, }; |
